﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using IntelligentDecisioning.SharePoint.Definition;
using System.Diagnostics;
using System.Reflection;
using IntelligentDecisioning.SharePoint.Logging;
using IntelligentDecisioning.SharePoint.Definition.Fields;
using System.Collections.Specialized;

namespace IntelligentDecisioning.SharePoint.Helpers
{
    /// <summary>
    /// A utility class containing object model code designed to manipulate
    /// sharepoint lists
    /// </summary>
    public class IDListHelper:IDSharePointHelper
    {
        #region Constructors

        public IDListHelper()
            : base(SPContext.Current.Site, SPContext.Current.Web, new NoLog())
        {

        }

        public IDListHelper(SPSite siteCollection, SPWeb targetWeb)
            : base(siteCollection, targetWeb, new NoLog())
        {

        }


        public IDListHelper(SPSite siteCollection, SPWeb targetWeb, ILog log)
            : base(siteCollection, targetWeb, log)
        {

        }

        #endregion

        #region Provision Lists

        public void ProvisionLists(List<SharePointList> lists)
        {
            Log.WriteToLog("There are " + lists.Count + " lists to provision");

            foreach (SharePointList list in lists)
            {
                CreateList(list);
            }
        }

        #endregion

        #region Provision List Views

        /// <summary>
        /// Called internally while we are provisioning a list to add any views that are required
        /// </summary>
        /// <param name="views"></param>
        /// <param name="targetList"></param>
        internal void ProvisionListViews(List<SharePointListView> views, SPList targetList)
        {
            Log.WriteToLog("There are " + views.Count + " views to provision");

            foreach (SharePointListView view in views)
            {
                CreateListView(view, targetList);
            }

            targetList.Update();

        }

        #region Create List View

        private void CreateListView(SharePointListView view, SPList targetList)
        {
            try
            {
                Log.WriteToLog("Attempting to create list view " + view.ViewName);

                targetList.Views.Add(view.ViewName,
                                    view.GetAllInternalFieldNames(),
                                    view.Filter,
                                    view.PageSize,
                                    true,
                                    view.IsDefaultView,
                                    view.ViewType,
                                    view.IsPersonalView);
            }
            catch (Exception e)
            {
                Log.WriteToLog("Unexpected error creating list view " + view.ViewName, e);
                throw;
            }
        }

        #endregion

        #endregion

        #region Create List

        /// <summary>
        /// Provision a sharepoint list
        /// </summary>
        /// <param name="list">The definition of the list to provision</param>
        private void CreateList(SharePointList list)
        {
            try
            {
                Log.WriteToLog("Attempting to create list " + list.Name);

                // Delete existing list if in overwrite mode
                if (list.OverwriteIfPresent && ListExists(list.Name))
                {
                    Log.WriteToLog(String.Format("List {0} already exists. Deleting", list.Name));
                    DeleteList(list.Name);
                    
                }

                // We only create a list if it doesnt exist (or we deleted it in the previous step)
                if (ListExists(list.Name) == false)
                {
                    Guid listId = TargetWeb.Lists.Add(list.Name, list.Description, list.TemplateType);
                    SPList sharePointList = TargetWeb.Lists[listId];
                    SPListTemplateCollection templates = TargetWeb.ListTemplates;
                   
                    sharePointList.OnQuickLaunch = list.OnQuickLaunch;

                    // If the list is content type enabled then add existing content types
                    if (list.EnableContentTypes == true)
                    {
                        IDContentTypeHelper contentTypeHelper = new IDContentTypeHelper(SiteCollection, TargetWeb);
                        sharePointList.ContentTypesEnabled = true;
                        foreach (SPContentTypeId contentTypeId in list.ContentTypes)
                        {
                            Log.WriteToLog("Adding content type" + contentTypeId);
                            sharePointList.ContentTypes.Add(contentTypeHelper.GetContentType(contentTypeId));
                        }
                    }
                    else
                    {
                        // Add any custom fields
                        if (list.Fields != null)
                        {
                            foreach (Field field in list.Fields)
                            {
                                Log.WriteToLog("Adding column " + field.Name);
                                sharePointList.Fields.AddFieldAsXml(field.GetProvisioningXML(),true, SPAddFieldOptions.Default);
                            }
                        }

                        // Add any site columns
                        if (list.SiteColumns != null)
                        {
                            IDSiteColumnHelper siteColumnHelper = new IDSiteColumnHelper(SiteCollection, TargetWeb);
                            foreach (Field siteColumn in list.SiteColumns)
                            {
                                // Provision the site column if it doesnt already exist
                                if (siteColumnHelper.SiteColumnExists(siteColumn.Id) == false)
                                {
                                    siteColumnHelper.ProvisionSiteColumn(siteColumn);
                                }

                                //foreach (SPField field in TargetWeb.AvailableFields)
                                //{
                                //    Log.WriteToLog(field.SchemaXml);
                                //}

                                // Add the site column to the list
                                SPField siteColumnField =  RootWeb.Fields[new Guid(siteColumn.Id)];
                                sharePointList.Fields.Add(siteColumnField);
                            }
                        }
                    }

                    // Versioning
                    sharePointList.EnableVersioning = list.EnableVersioning;
                    if (list.EnableVersioning)
                    {
                        try
                        {
                            sharePointList.EnableMinorVersions = list.MinorVersions;
                        }
                        catch (Exception e)
                        {
                            //Exception thrown here as some lists don't support the minor versioning
                            Log.WriteToLog("Error Enabling Minor Versions on list " + list.Name, e);
                        }
                    }
                    

                    // Approval
                    sharePointList.EnableModeration = list.RequireContentApproval;

                    // Email enabled
                    if(list.EmailEnabled)
                    {
                        sharePointList.EnableAssignToEmail = list.EmailEnabled;
                        sharePointList.EmailAlias = list.EmailAlias;
                    }
                    

                    sharePointList.Update(); // save the changes to the newly created list


                    Log.WriteToLog("List " + list.Name + " Created sucessfully");


                    // Create and views defined for the list
                    ProvisionListViews(list.Views, sharePointList);

                    // Add any event handlers defined for the list
                    ProvisionListEventHandlers(list.EventHandlers, sharePointList);

                    // Add any workflow associations for the list
                   
                }
                else
                {
                    Log.WriteToLog("List " + list.Name + " already exists and has not be overwritten");
                }
            }
            catch (Exception e)
            {
                Log.WriteToLog("Unexpected error creating list " + list.Name, e);
                throw;
            }
        }

        private void ProvisionListEventHandlers(List<SharePointListItemEventHandler> eventHandlers, SPList sharePointList)
        {
            if (eventHandlers.Count > 0)
            {
                foreach (SharePointListItemEventHandler eventHandler in eventHandlers)
                {
                    eventHandler.TargetList = sharePointList;
                }

                ProvisionListEventHandlers(eventHandlers);
            }
        }

        private void BodgeForChoiceFields(SharePointList list, SPList sharePointList)
        {
            foreach (Field field in list.Fields)
            {
                ChoiceField choiceFieldDefinition = field as ChoiceField;

                if (choiceFieldDefinition != null)
                {
                    SPFieldChoice choiceField = sharePointList.Fields[field.DisplayName] as SPFieldChoice;
                    if (choiceField != null)
                    {
                        foreach(String choice in choiceFieldDefinition.Choices)
                        {
                            choiceField.Choices.Add(choice);
                        }
                        choiceField.Update();
                    }
                }
            }

            sharePointList.Update();
        }

        #endregion

        #region List Exists

        /// <summary>
        /// A rather ineligant way to check if a list exists in the current web
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool ListExists(string name)
        {
            try
            {
                SPList list = TargetWeb.Lists[name];
                return true;
            }
            catch
            {
                return false;
            }
        }

        #endregion

        #region Get List Methods

        /// <summary>
        /// If the list doesnt exist then an exception will be generated
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public SPList GetList(String name)
        {
            //TODO: Clever error handling
            return TargetWeb.Lists[name];
        }

        public SPList GetList(Guid id)
        {
            //TODO: clever error handling
            return TargetWeb.Lists[id];
        }

        #endregion

        #region Delete List

        public void DeleteList(String name)
        {
            if (ListExists(name))
            {
                SPList list = GetList(name);
                list.Delete();
                TargetWeb.Update();
            }
        }


        #endregion

        #region Provision List Event Handlers

        /// <summary>
        /// A generic method to provision an event handler to a sharepoint list.
        /// This method assumes that the event handlers are available in the currently
        /// executing assembly unless explicitly set.
        /// </summary>
        /// <param name="list"></param>
        internal void ProvisionListEventHandlers(List<SharePointListItemEventHandler> eventHandlers)
        {
            Log.WriteToLog("There are " + eventHandlers.Count + " ListItemEventHandlers to provision");

            // Attempt to register each event item receiver specified in the current feature
            foreach (SharePointListItemEventHandler eventHandler in eventHandlers)
            {
                try
                {
                    // We assume the current assembly and override if expliticly set
                    String assemblyName = Assembly.GetExecutingAssembly().FullName;
                    if (eventHandler.AssemblyNameIsSet()) assemblyName = eventHandler.AssemblyName;

                    // We currently only support registration on list name
                    if (eventHandler.RegistrationType == RegistrationType.Name)
                    {
                        if (ListExists(eventHandler.ListName))
                        {
                            SPList targetList = eventHandler.TargetList;
                            if (targetList == null)
                            {
                                targetList = GetList(eventHandler.ListName);
                            }
                                
                            SPEventReceiverDefinition defToDelete = null;

                            // Delete this event receiver if it already exisits
                            foreach (SPEventReceiverDefinition def in targetList.EventReceivers)
                            {
                                if(def.Class.Equals(eventHandler.ClassName))
                                {
                                    Log.WriteToLog("Event receiver is already registered.. deleting");
                                    defToDelete = def;
                                }
                            }

                            if (defToDelete != null)
                            {
                                defToDelete.Delete();
                            }

                            targetList.Update();

                            // Add each action specified
                            foreach (SPEventReceiverType eventReceiverType in eventHandler.EventReceiverTypes)
                            {
                                Log.WriteToLog("Registering " + assemblyName + " : " + eventHandler.ClassName + " " +  eventReceiverType.ToString() + " for list " + targetList.Title); 
                                targetList.EventReceivers.Add(eventReceiverType, assemblyName, eventHandler.ClassName);
                                Log.WriteToLog("Event Receiver registered successfully");

                                #region Demo Code

                                if (eventHandler.ClassName.Contains("StudySiteItemEventReceiver"))
                                {
                                    try
                                    {
                                        Log.WriteToLog("Testing content type code");
                                        IDContentTypeHelper contentTypeHelper = new IDContentTypeHelper(base.SiteCollection, base.TargetWeb);
                                        SPContentType contentType = contentTypeHelper.GetContentType();
                                        contentType.ReadOnly = false;

                                        if (contentType != null)
                                        {
                                            //SPEventReceiverDefinition receiverDefinition = contentType.EventReceivers.Add();
                                            //receiverDefinition.Name = "Test";
                                            //receiverDefinition.Type = SPEventReceiverType.ItemAdding;
                                            //receiverDefinition.Assembly = assemblyName;
                                            //receiverDefinition.Class = eventHandler.ClassName;
                                            //receiverDefinition.Update();

                                            try
                                            {

                                                contentType.EventReceivers.Add(eventReceiverType, assemblyName, eventHandler.ClassName);
                                                contentType.Update(true);
                                            }
                                            catch(Exception ex)
                                            {
                                                Log.WriteToLog("First attempt failed with " + ex);
                                                SPEventReceiverDefinition receiverDefinition = contentType.EventReceivers.Add();
                                                receiverDefinition.Name = "Test";
                                                receiverDefinition.Type = SPEventReceiverType.ItemAdding;
                                                receiverDefinition.Assembly = assemblyName;
                                                receiverDefinition.Class = eventHandler.ClassName;
                                                receiverDefinition.SequenceNumber = 100001;
                                                receiverDefinition.Update();

                                                Log.WriteToLog("Receiver definition code executed sucessfully");
                                            }

                                            Log.WriteToLog("Content type updated with event receiver");
                                        }
                                        else
                                        {
                                            Log.WriteToLog("Content type was not found in site");
                                        }

                                    }
                                    catch (Exception e)
                                    {
                                        Log.WriteToLog("Error registering content type event handler", e);
                                    }
                                }
                                else
                                {
                                    Log.WriteToLog("There are no content type event receivers");
                                }

                                #endregion

                            }
                            targetList.Update();
                        }
                        else
                        {
                            // Oops - the list does not exist
                            String errorMessage = String.Format("Cannot register event handler {0} on list name {1}. The specified list does not exist.", eventHandler.ClassName, eventHandler.ListName);
                            throw new ArgumentException(errorMessage);
                        }
                    }
                    else
                    {
                        // someday we'll find a way
                        // but not today as its nearly beer o'clock
                        Log.WriteToLog("id-sharepoint curently only supports the registration of event handlers on named lists");
                    }
                }
                catch (Exception e)
                {
                    Log.WriteToLog("Unexpected error provisioning event handler" + e);
                    throw;
                }
                
            }
        }

        #endregion
    }
}
